/*
 * @Description: 分片上传
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2021-11-10 16:07:09
 * @LastEditTime: 2021-11-18 16:47:25
 */

/**
 * 这里只是简单处理，不计算整个文件的MD5值
 * 只要文件的lastModified、name、size、type、当前用户一致，就认为是同一文件
 */
import request from '@/utils/request2'
import MD5 from 'spark-md5'
import { getFileShardByRecord, addFileSplit } from '@/api/bi/data-link2'

// 传入file对象
export default async (vm, parentId) => {
    if (vm.nodeInfo.type === 1) {
        if (vm.isSaveAllSheet) {
            let files = []
            let shardVoList = []
            for (let key in vm.currFile) {
                let file = vm.sheet2Csv(vm.currFile[key])
                files.push({
                    key,
                    file
                })
                shardVoList.push(takeFileInfo(file, vm, key))
            }
            let promAll = files.map(({ key, file }, index) => chunkFileUpload(file, vm, parentId, key, shardVoList, index))
            let reqResArr = await Promise.all(promAll)
            if (reqResArr.filter(res => res.code && res.progress >= 100).length == reqResArr.length) {
                return reqResArr[0]
            } else {
                return reqResArr.find(res => !res.code)
            }
        } else {
            let _file = vm.sheet2Csv()
            return chunkFileUpload(_file, vm, parentId, vm.excelSheet, [takeFileInfo(_file, vm, vm.excelSheet)], 0)
        }
    } else {
        let __file = vm.fileList[0]
        return chunkFileUpload(__file, vm, parentId, null, [takeFileInfo(__file, vm)], 0)
    }
}

/**
 * 单文件信息整理
 */
function takeFileInfo (File, vm, thesheet) {
    const { lastModified, name: filename, size, type } = File
    const limitSize = comChunkLimitSize(size)
    return {
        fileHash: MD5.hash(lastModified + filename + size + type),
        fileName: filename,
        fileSeparator: vm.fileSeparator,
        fileSeparatorType: vm.fileSeparatorType,
        size,
        fileSize: (size / 1024 / 1024).toFixed(2) > 0 ? (size / 1024 / 1024).toFixed(2) + ' MB' : (size / 1024).toFixed(2) + ' kB',
        fileType: vm.nodeInfo.type,
        limitSize, //每个文件切片大小
        shardCount: Math.ceil(size / limitSize), //文件切片总数
        sheetName: thesheet || 'Sheet1',
        preData: JSON.stringify(vm.nodeInfo.type === 1 ? vm.getPreExcelData10(thesheet) : vm.csvTableData)
    }
}

/**
 * 分片上传
 */
async function chunkFileUpload (File, vm, parentId, thesheet, shardVoList, _index) {
    const { fileHash, fileName, size, limitSize, shardCount } = shardVoList[_index]

    // 唯一标识md5
    const currFileUpItemEl = document.querySelector(`.u-${fileHash}`)
    if (currFileUpItemEl) {
        return {
            code: 0,
            errorMsg: '文件已在上传队列',
        }
    }

    if (size == 0) {
        return {
            code: 0,
            errorMsg: '请勿上传空文件',
        }
    }

    // 这里预先请求后台，判断当前文件的上传情况（同步）
    const fileUpInfo = await getFileShardByRecord({ fileHash })

    // 分段的开始结束点位
    let startIndex = 0, endIndex;
    // 当前分段号，从0开始
    let chunkNum = 0;
    // 已经上传的分段号
    let hasUploadeds = []
    // 上传结果对象
    let uploadResult = {
        code: 1,
        filename: thesheet ? thesheet + '_' + fileName : fileName,
        errorMsg: null,
        progress: 0
    }
    // 监听
    for (let key in uploadResult) {
        // 劫持
        objProxy(uploadResult, key, function () {
            if (uploadResult.progress <= 100) {
                uploadView(uploadResult, fileHash)
            }
        })
    }

    // 上传图示
    uploadView(uploadResult, fileHash)

    // 全部已经上完成
    if (fileUpInfo && fileUpInfo.data && fileUpInfo.data.length == shardCount) {
        uploadResult.progress = 100

        return uploadResult
    } else if (fileUpInfo && fileUpInfo.data && fileUpInfo.data.length < shardCount) {
        // 计算剩余需要上传的部分
        // 已经传过的分段号
        hasUploadeds = fileUpInfo.data.map(item => item.shardNum)
    }

    if (_index == 0 && (!fileUpInfo || !fileUpInfo.data || !fileUpInfo.data.length)) {
        // 文件信息保存
        await addFileSplit({
            dbSourceName: vm.formData.dbSourceName,
            parentId,
            type: vm.nodeInfo.type,
            shardVoList
        })
    }

    // 分段请求集合
    const chunkReqArr = []

    // 循环请求上传
    while (startIndex < size) {
        // 计算开始结束点位
        endIndex = startIndex + limitSize;
        if (endIndex > size) {
            endIndex = size;
        }

        //切割文件
        const chunk = File.slice(startIndex, endIndex);

        // 拼接参数
        const formData = new FormData();
        formData.append("excelFiles", chunk, fileName);
        formData.append("fileHash", fileHash);
        formData.append("shardNum", chunkNum);
        formData.append("shardSize", endIndex - startIndex);


        // 上传分段
        if (!hasUploadeds.includes(chunkNum)) {
            chunkReqArr.push(uploadFileChunk(formData, uploadResult, chunk.size / size))
        }

        // 点位更新
        startIndex = endIndex;
        // 分段更新
        chunkNum++;
    }

    try {
        const reqResArr = await Promise.all(chunkReqArr)
        const errRes = reqResArr.find(item => item.code != 200)
        if (!errRes) {
            // 全部上传完毕
            if (uploadResult.progress < 100) uploadResult.progress = 100
            return uploadResult
        } else {
            uploadResult.errorMsg = '上传出错'
            console.error(errRes.msg)
        }

    } catch (error) {
        uploadResult.errorMsg = '上传出错'
    }

    return uploadResult
}


/**
 * 上传结果监听，闭包封装
 */
function objProxy (obj, key, callback) {
    var val = obj[key]
    Object.defineProperty(obj, key, {
        set: function (newVal) {
            if (key == 'progress') {
                newVal = Number(Number(newVal).toFixed(2))
            }
            val = newVal
            callback()
        },
        get: function () {
            return val
        }
    })
}

/**
 * 分段大小计算
 */
function comChunkLimitSize (filesize) {
    let filesizeMb = (filesize / 1024 / 1024).toFixed(4)

    if (filesizeMb <= 100) {
        // 10M
        return 1024 * 1024 * 10
    }

    if (filesizeMb > 100 && filesizeMb <= 500) {
        // 30M
        return 1024 * 1024 * 30
    }

    if (filesizeMb > 500) {
        // 100M
        return 1024 * 1024 * 100
    }
}

/**
 * 上传分段（异步）
 */
function uploadFileChunk (data, uploadResult, sizePerc) {
    let progressFlag = 0
    return request({
        url: '/bi/data/uploadFileShard',
        method: 'post',
        data,
        noLoading: true,
        onUploadProgress (progressEvent) {
            // 上传进度更新
            uploadResult.progress += sizePerc * ((progressEvent.loaded - progressFlag) / progressEvent.total) * 100
            progressFlag = progressEvent.loaded
        }
    })
}

/**
 * 上传进度图示
 */
function uploadView (uploadResult, hash) {
    var uploadViewEl = document.querySelector('.file-chunk-upload')

    if (!uploadViewEl) {
        const El = document.createElement('div')
        El.setAttribute('class', 'file-chunk-upload')
        El.setAttribute('style', `position:fixed;bottom:5px;right:5px;background:#304156;width:410px;height:auto;box-shadow:2px 2px 7px #000;z-index:9999;font-size:12px;padding:0 10px;color:#fff`)
        let item = createFileLoaderItem(uploadResult, hash)[0]

        El.appendChild(item)

        document.body.appendChild(El)

        uploadViewEl = El
    } else {
        let [item, has] = createFileLoaderItem(uploadResult, hash)
        if (!has) {
            uploadViewEl.appendChild(item)
        }

        item.querySelector('span.bar').style.width = uploadResult.progress > 100 ? 100 : uploadResult.progress + '%'
        item.querySelector('span.chunk-upload-progress').innerText = uploadResult.progress > 100 ? 100 : uploadResult.progress


        if (uploadResult.progress >= 100) {
            item.querySelector('span.chunk-upload-status').innerText = '上传完成'
            setTimeout(() => {
                item.remove()
            }, 1000)
        }

        if (uploadResult.errorMsg) {
            let $upstatus = item.querySelector('span.chunk-upload-status')
            $upstatus.innerText = uploadResult.errorMsg
            $upstatus.style.color = "#f44336"

        }
    }

    if (uploadViewEl.innerHTML == "") {
        uploadViewEl.remove()
    }
}

/**
 * 创建子项上传进度显示
 */
function createFileLoaderItem (uploadResult, hash) {
    var currFileUpItemEl = document.querySelector(`.u-${hash}`)
    if (!currFileUpItemEl) {
        const El = document.createElement('div')
        El.setAttribute('class', `u-${hash}`)
        El.setAttribute('style', `padding: 8px 0;`)

        const filenameStyle = `width:185px;overflow:hidden;height:12px;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;word-break:break-all;`
        const barStyle = `width:0;position:absolute;height:100%;background:yellow;`

        El.innerHTML = `
            <div style="width:100%;height:5px;background:black;margin-bottom:5px;position:relative;"><span class="bar" style="${barStyle}"></span></div>
            <div class="info" style="display: flex;justify-content: space-between;align-items: center;line-height:1;">
                <div style="${filenameStyle}">文件：${uploadResult.filename}</div>
                <div style="width:90px;">进度：<span class="chunk-upload-progress">${uploadResult.progress}</span>%</div>
                <div>状态：<span class="chunk-upload-status">正在上传</span></div>
                <div onclick="this.parentElement.parentElement.remove()" style="cursor:pointer">×</div>
            </div>
        `
        return [El, false]
    } else {
        return [currFileUpItemEl, true]
    }
}